Introduction to Spatial Transcriptomics


1. What is spatial transcriptomics?

1.1. Basic principles of spatial transcriptomics

1.1a. Probe-based approaches

1.1b. Sequencing-based approaches

1.2. Comparison to single-cell transcriptomics

2. Setting up our R environment for data exploration

In preparation for the following hands-on experiences we will set up an R environment which has everything we need.

2.1. Load libraries

suppressPackageStartupMessages({
  library(Seurat)
  library(ggplot2)
  library(tidyr)
  library(scDblFinder)
  library(SingleCellExperiment)
})

2.2. Import data

For this tutorial we will use raw unprocessed data from human peripheral blood mononuclear cells (PBMCs) generated using the 10X Genomics platform which can be download from the Seurat page. These data should be downloaded, extracted, and placed in the downloads folder for this workshop.

# Load the PBMC dataset
pbmc.data <- Read10X(data.dir = "./downloads/filtered_gene_bc_matrices/hg19/")
# Initialize the Seurat object with the raw (non-normalized data).
seurat_object <- CreateSeuratObject(counts = pbmc.data, project = "pbmc3k", 
                                    min.cells = 3, min.features = 200)

Note: We apply some preliminary filters as we know that GEMs which very low numbers of genes or genes which are barely detected are unlikely to be actual cells.

2.3. Check that everything was loaded correctly

print(seurat_object)

3. Overview of Single-cell Data Structure

To better understand how to work with single-cell/nuclei RNA sequencing data we will take a deep dive into how datasets are typically structured. In the diagram below we illustrate how droplet-based scRNAseq approaches separate cells and their mRNAs. But how does this translate into data?

Generated using biorender.com
Generated using biorender.com

3.1. The Gene x Cell Matrix

# Raw counts in a seurat object is saved in the assays-RNA-counts slot 
raw_counts = as.matrix(seurat_object@assays$RNA@layers$counts)
rownames(raw_counts) = rownames(seurat_object)
colnames(raw_counts) = colnames(seurat_object)

# Lets look at the first 4 columns and rows
print(raw_counts[1:4,1:4])

What does this mean?

Columns: The columns represent a unique barcode. As we can see below, this barcode marks an individual cell which is how we can identify each one. So in this object, the columns are the cells

Generated in biorender.com and adapted from Danielski, K. (2023). Guidance on Processing the 10x Genomics Single Cell Gene Expression Assay. In: Calogero, R.A., Benes, V. (eds) Single Cell Transcriptomics. Methods in Molecular Biology, vol 2584. Humana, New York, NY. https://doi.org/10.1007/978-1-0716-2756-3_1
Generated in biorender.com and adapted from Danielski, K. (2023). Guidance on Processing the 10x Genomics Single Cell Gene Expression Assay. In: Calogero, R.A., Benes, V. (eds) Single Cell Transcriptomics. Methods in Molecular Biology, vol 2584. Humana, New York, NY. https://doi.org/10.1007/978-1-0716-2756-3_1

Rows: We see that the first few rows have names such as RP11-206L10.9. A quick google search will tell you that this is a human gene (Ensembl ID - ENSG00000237491). So we see in this object that the rows represent the genes.

3.2. Counting the number of cells and genes

Single-cell transcriptomic analysis tools (e.g., Seurat) does a lot of the heavy lifting for us when it comes to counts and other summary data information. For example, the following code will tell you the number of genes and cells, as well store raw, normalized, and scaled data, dimension reductions, and other analyses which may have been performed.

print(seurat_object)

This indicates that we have 13,714 genes (features) across 2,700 samples.

However, we can also extract that information from the count table:

print(paste0('Cells: ', ncol(raw_counts)))
print(paste0('Features:', nrow(raw_counts)))

These numbers should match!

3.3. A deeper dive into counts

3.3a. What is sequencing depth?

# Calculate the total reads in each individual cell
read_depths = colSums(raw_counts)

# How are they distributed?
hist(read_depths, main = "Histogram of read depth", 
     xlab = "Depth", ylab = "Frequency",
     breaks = 200)

This histogram shows that the number of mRNA transcripts detected in each cell ranged from 546 to 15,818, but that most were around 2,000. So what might account for these differences?

  • Cell types: Some cell types may simply just express more mRNA than others. For example, Hepatocytes are known to contain a lot of RNA while others may not.

  • Stochastic gene expression: Fluctuations in transcription and degradation from normal biological processes, cell states, and other factors in individual cells which have very low amounts of RNA to begin can appear as big differences.

  • Technical variability: Sequencing depth, assay kits, and other technical factors may contribute to these differences.

The zero-inflation debate: In the early days of scRNAseq it was a widely accepted that data was zero-inflated, i.e., it had more zero’s than it should because of technical challenges. Today, we better understand that the large number of zero’s are not only technical, but in fact does reflect true biology as well. The following manuscript delves deeper into the debate: Jiang et al., 2022

3.3b. Another way to look at depth

Sequencing depth ultimately drives the ability to detect individual genes in a cell or sample (bulk RNA seq). As more reads are performed on a sample, the probability of capturing a lower abundance gene increases. Therefore, sequencing depth can have a significant impact on the end results of your sequencing results.

So why not sequence as deep as possibe? As with anything, we need to balance cost with value. The following figures are standard outputs from scRNAseq reports including those linked above.

As you can see, the more reads we have per cell, the smaller the slop for capturing new genes gets. Therefore, the difference between capturing 80% of genes and 100% could mean 2, 10, or even >100X the amount of sequencing. Is it truly worth it for capturing a gene who expression is so low? This is why we limit our depth for sequencing and why most datasets produced with the sample platform will tend to detect a similar number of genes.

3.4. Common Storage Formats

3.4a. Sparse tables (raw)

We previously mentioned that single-cell/nuclei transcriptomic data has a lot of zeros. Lets actually look at the count distribution so you can see:

# We wan to collapse all the values into a vector of values
mat_values <- as.vector(raw_counts)

# Plot histogram
hist(mat_values, main = "Histogram of Matrix Values", xlab = "Value", ylab = "Frequency", breaks = 600, xlim = c(0,10))

This is why you will almost always find raw scRNAseq data saved as a .mtx (sparse matrix) file or format.

Here we will download publicly accessible sparse matrices from the Gene Expression Omnibus, an excellent resource for obtaining raw data if you want to reuse and reanalyze existing data. This dataset is from single-nuclei RNA sequencing of livers from mice treated with the environmental contaminant 2,3,7,8-tetrachlorodibenzo-p-dioxin (TCDD).

#download.file('https://www.ncbi.nlm.nih.gov/geo/download/?acc=GSE184506&format=file&file=GSE184506%5Fsct%2Emtx%2Egz', './downloads/matrix.mtx.gz')
system("gunzip ./downloads/matrix.mtx.gz")

#download.file('https://www.ncbi.nlm.nih.gov/geo/download/?acc=GSE184506&format=file&file=GSE184506%5Fsct%2Ebarcodes%2Etsv%2Egz', './downloads/barcodes.tsv.gz')
system("gunzip ./downloads/barcodes.tsv.gz")

#download.file('https://www.ncbi.nlm.nih.gov/geo/download/?acc=GSE184506&format=file&file=GSE184506%5Fsct%2Egenes%2Etsv%2Egz', './downloads/genes.tsv.gz')
system("gunzip ./downloads/genes.tsv.gz")
# Read the first few lines of the file
lines <- readLines('./downloads/matrix.mtx', n = 10)

# Print the lines
cat(lines, sep = "\n")

As you can see, there is not a zero in sight here. After you read the first 2 summary lines you can see the following structure:

Feature/Gene Barcode/Cell Count/Value
Refers to the row number in the genes.tsv file Refers to the row number in the barcodes.tsv file Include all non-zero values of the dataset

Let’s see what this is here

# Read the specific line of the file
readLines('./downloads/genes.tsv', n = 15)[15]

# Read the specific line of the file
readLines('./downloads/barcodes.tsv', n = 1)[1]

From here we can see that the first row of data in the mtx file represent the gene Sntg1 for the cell with barcode AAACCCACAACTTCTT_1.

3.4b. Seurat object

Image from https://sib-swiss.github.io/single-cell-training/day1/day1-2_analysis_tools_qc.html

Seurat objects are probably the most commonly used to store and organize scRNAseq data for R users. So far this is what we have used for the published PBMC data which we imported in Section 2. Let’s take another closer look at this object:

print(slotNames(seurat_object)) # Lets examine all the slots

There is a lot of information, but the ones you will most commonly use if you do single-cell analyses in R are assays, meta.data, and reductions

  • Assays will hold the count data

  • Meta.data will hold the metadata for each individual cell/nuclei

  • Reductions will hold the dimension reductions for visualizations

3.4c. SingleCellExperiment

Another common format in R are SingleCellExperiments. They contain the same information, but are organized in a different manner.

image from https://bioconductor.org/books/3.13/OSCA.intro/the-singlecellexperiment-class.html

First, let’s convert our seurat object to SingleCellExperiment. Most formats can easily be interchanged:

sce = as.SingleCellExperiment(seurat_object)

Next, lets see what the structure looks like:

print(sce)

Here we can see:

  • rownames and rowData contain the gene information and metadata

  • colnames and colData contain the cell information and metadata

  • assays here also hold the count data

We can examine the pieces of data such as the cell metadata as follows:

print(colData(sce))

3.4d. AnnData/H5AD (Python)

This workshop will not go into python resources, but it is imporant to note that Python and R are the two most common programming languages for single-cell transcriptomic analysis and it is not uncommon to have to use both. The H5ad file format is similar to the Seurat object and SingleCellExperiment object in the sense that it stores the different components of the data in an organized manner.

Image from: https://www.sc-best-practices.org/introduction/analysis_tools.html


4. Assessing data quality

Any analysis, no matter of size, should undergo rigorous QC. When it comes to single-cell transcriptomic data, there are several computational tools that help you in the process from enabling visualizations to performing a panel of QC tests and reporting several different metrics for the end user to evaluate. A list of just some of these tools can be found here.

Let’s go through some of the most common QC metrics, what to they mean, and why do they matter?

4.1. Genes and transcripts

How does the number of detected genes and transcripts indicate quality? While we don’t have a known number of expressed genes for each cell type, we do know that cells require the expression of hundreds to thousands of genes to function properly.

Number of genes (features)

We can check these values by running the code below:

VlnPlot(seurat_object, features = 'nFeature_RNA') + NoLegend()

Here we can see that (1) the number of genes detected ranges from ~200 - 3000 and (2) most express ~900 genes. Is this normal and expected? In reality, there is no universal threshold, these values will depend on the cell type and the sequencing depth. But we do know that if most cells had extemely low counts that either cells were not healthy/intact or sequencing depth was much too low. It could also point to errors further upstream such as alignment to the incorrect reference genome.

Number of mRNA molecules (count)

We can also examine the total number of mRNA molecules detected:

VlnPlot(seurat_object, features = 'nCount_RNA')

As above, we can see a good distribution centered around 2500 mRNA molecules. These value should always be higher than the number of features.

Correlation of features and molecules

A third way to look at this data is to examine the correlation of features and mRNA molecules:

FeatureScatter(object = seurat_object, feature1 = 'nCount_RNA', feature2 = 'nFeature_RNA')

This metric is more informative because it tells you that as you detect more mRNA molecules, you also detect more different genes. This would generally be expected as you are getting a larger sampling of the mRNA resulting in an increased chance of sampling different genes. However, when you assess these plots, you will want to keep the biology in mind.

4.2. Mitochondrial RNA

Here, biological knowledge is extremely important for evaluating this metric and removing cells based on a threshold. Consider these three experiments:

  • Nuclei isolated from muscle cells which require a lot of energy

  • Muscle whole cells which require a lot of energy

  • Skin cells which are less metabolically active

What would you expect to see (qualitative) in the amount of mitochondrial genes for each of these experiments?

Assessing percent mitochondria counts

We can assess the percentage of mitochondrial genes by calculating the number of counts coming from mitochondrial genes (usually prefixed with mt- or MT-) compared to the total counts. This can be visualized like this:

# Calcuate the percentage of mitochondrial counts
seurat_object[["percent.mt"]] <- PercentageFeatureSet(seurat_object, pattern = "^MT-")

# Plot the values
VlnPlot(seurat_object, features = 'percent.mt')

We see that the percentage of counts representing mitochondrial genes is mostly below 5% but as high as ~25%. To evaluate these results think back to the three experiments and consider what you would expect to see:

  • Nuclei isolated from muscle cells which require a lot of energy

    Despite being metabolically active, nuclei isolation is expected to wash out mitochondria. High contamination is indicative of high ambient RNA or mitochondria sticking to the the nuclei.

  • Muscle whole cells which require a lot of energy

    Given that muscle cells are highly metabolically active and are known to have among the most mitochondria among mammalian cells, these data may show higher percentage of mitochondrial genes than most datasets.

  • Skin cells which are less metabolically active

    Given that these are less metabolically active, they may be expected to show similar or lower percentage of mitochondrial genes.

Correlation of counts and mitochondrial counts

As with the features and counts, we can look at how these correlate:

FeatureScatter(seurat_object, feature1 = 'nCount_RNA', feature2 = 'percent.mt')

Interestingly, these are not correlated at all. Prior to filtering, these values will be anti-correlated as mitochondria often makes up the ambient RNA (release from dead or lysed cells) and will be more represented in the background (low count cells).

4.3. Ambient RNA contamination

As with any single-cell transcriptomic tool, there are dozens, if not more, options for removal of abient RNA. Generally, the concept for ambient RNA removal is the same - substract genes which are present in the empty GEMs/wells from those which are found to have cells. Other techniques such as combinatorial barcoding approaches have to make different assumptions.

SoupX is one example of a tool which cleans up the soup (floating ambient RNA) from cells. This tools uses the most raw format of data which is generate before a threshold is set on the knee plot discussed above. For 10X Genomics these are stored in the raw_feature_bc_matrix and filtered_feature_bc_matrix folders.

This tutorial will not go through the entire ambient RNA removal workflow. Excellent examples can be found here and here.

4.4 Doublets

Another common challenge with using single-cell transcriptomic platforms is one that can affect most single-cell based techniques including flow cytometry - doublets. These can be a result of cells sticking to each other or being too close to each other during the sorting, and the rate is typically a function of the total number of cells examined (more cells results in more doublets).

Note: We will discuss dimension reduction which is central to this approach in a later module.

Doublets can consist of either 2 cells of the same type (homotypic) or different cell types (heterotypic). As you can probably imagine, distinguising homotypic doublets is more challenging, but are generally considered to be less of a concern.

DoubletFinder and scDblFinder are R based tool for the identification of doublets based on the general principle outlined above. This example uses scDblFinder:

sce = as.SingleCellExperiment(seurat_object) # We need to convert the Seurat format to SingleCellExperiment format
sce <- scDblFinder(sce) # Run scDblFinder

Next we will transfer the information gained from this analysis into the Seurat object so that we can visualize it a later step.

# Transfer the doublet information to the seurat object
seurat_object$scDblFinder.class = colData(sce)$scDblFinder.class
seurat_object$scDblFinder.score = colData(sce)$scDblFinder.score

4.5. Filtering data

Ultimately, the purpose of the QC analyses is not only to understand your data and find any major issues. It is also used to identify individual data points which are likely problematic and could negatively impact subsequent analyses. Because we often have thousands of data points, we generally accept that some of these data points are technical noise which can simply be removed. For example, here we:

  • Remove cells with extremely high or low counts as either empty droplets or multiplets.

  • Remove cells with more than 5% of reads from mitochondrial genes as an indicator of unhealthy cells.

  • Remove cells classified as doublets.

seurat_object <- subset(seurat_object, subset = nFeature_RNA > 200 & nFeature_RNA < 2500 & percent.mt < 5 & scDblFinder.class == 'singlet')

print(seurat_object)

5. Normalization

Finally we want to address the issue of unequal sequencing depth and other technical noise by performing normalization. Here we normalize the data using a regularized negative binomial regression which models adjusts the counts for each gene based on the total number of counts within a cell with adjustments for lowly and highly expressed genes as described here.

seurat_object = SCTransform(seurat_object, verbose = F)

Finally we want to visualize the data. To do this we need to perform dimension reduction which will be covered in a later module. We will run it here for visualization purposes.

seurat_object = RunPCA(seurat_object, verbose = F)
seurat_object = RunUMAP(seurat_object, dims = 1:30, verbose = F)
FeaturePlot(seurat_object, features = c('scDblFinder.score', 'percent.mt', 'nFeature_RNA', 'nCount_RNA'))
6. Dimension reduction and clustering; 7. Annotation of cell types; 8. Trajectories and RNA velocity; 9. Inferring cell-cell communication; 10. Spatial transcriptomics
LS0tDQp0aXRsZTogIkludHJvZHVjdGlvbiB0byBzY1JOQXNlcSINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQoNCiMgSW50cm9kdWN0aW9uIHRvIFNwYXRpYWwgVHJhbnNjcmlwdG9taWNzDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyAxLiBXaGF0IGlzIHNwYXRpYWwgdHJhbnNjcmlwdG9taWNzPyANCg0KIyMjIDEuMS4gQmFzaWMgcHJpbmNpcGxlcyBvZiBzcGF0aWFsIHRyYW5zY3JpcHRvbWljcw0KDQojIyMjIDEuMWEuIFByb2JlLWJhc2VkIGFwcHJvYWNoZXMNCg0KIyMjIyAxLjFiLiBTZXF1ZW5jaW5nLWJhc2VkIGFwcHJvYWNoZXMNCg0KIyMjIDEuMi4gQ29tcGFyaXNvbiB0byBzaW5nbGUtY2VsbCB0cmFuc2NyaXB0b21pY3MNCg0KIyMgMi4gU2V0dGluZyB1cCBvdXIgUiBlbnZpcm9ubWVudCBmb3IgZGF0YSBleHBsb3JhdGlvbg0KDQpJbiBwcmVwYXJhdGlvbiBmb3IgdGhlIGZvbGxvd2luZyBoYW5kcy1vbiBleHBlcmllbmNlcyB3ZSB3aWxsIHNldCB1cCBhbiBSIGVudmlyb25tZW50IHdoaWNoIGhhcyBldmVyeXRoaW5nIHdlIG5lZWQuDQoNCiMjIyAqKjIuMS4gTG9hZCBsaWJyYXJpZXMqKg0KDQpgYGB7cn0NCnN1cHByZXNzUGFja2FnZVN0YXJ0dXBNZXNzYWdlcyh7DQogIGxpYnJhcnkoU2V1cmF0KQ0KICBsaWJyYXJ5KGdncGxvdDIpDQogIGxpYnJhcnkodGlkeXIpDQogIGxpYnJhcnkoc2NEYmxGaW5kZXIpDQogIGxpYnJhcnkoU2luZ2xlQ2VsbEV4cGVyaW1lbnQpDQp9KQ0KYGBgDQoNCiMjIyAqKjIuMi4gSW1wb3J0IGRhdGEqKg0KDQpGb3IgdGhpcyB0dXRvcmlhbCB3ZSB3aWxsIHVzZSByYXcgdW5wcm9jZXNzZWQgZGF0YSBmcm9tIGh1bWFuIHBlcmlwaGVyYWwgYmxvb2QgbW9ub251Y2xlYXIgY2VsbHMgKFBCTUNzKSBnZW5lcmF0ZWQgdXNpbmcgdGhlIDEwWCBHZW5vbWljcyBwbGF0Zm9ybSB3aGljaCBjYW4gYmUgZG93bmxvYWQgZnJvbSB0aGUgW1NldXJhdCBwYWdlXShodHRwczovL3NhdGlqYWxhYi5vcmcvc2V1cmF0L2FydGljbGVzL3BibWMza190dXRvcmlhbCkuIFRoZXNlIGRhdGEgc2hvdWxkIGJlIGRvd25sb2FkZWQsIGV4dHJhY3RlZCwgYW5kIHBsYWNlZCBpbiB0aGUgZG93bmxvYWRzIGZvbGRlciBmb3IgdGhpcyB3b3Jrc2hvcC4NCg0KYGBge3J9DQojIExvYWQgdGhlIFBCTUMgZGF0YXNldA0KcGJtYy5kYXRhIDwtIFJlYWQxMFgoZGF0YS5kaXIgPSAiLi9kb3dubG9hZHMvZmlsdGVyZWRfZ2VuZV9iY19tYXRyaWNlcy9oZzE5LyIpDQojIEluaXRpYWxpemUgdGhlIFNldXJhdCBvYmplY3Qgd2l0aCB0aGUgcmF3IChub24tbm9ybWFsaXplZCBkYXRhKS4NCnNldXJhdF9vYmplY3QgPC0gQ3JlYXRlU2V1cmF0T2JqZWN0KGNvdW50cyA9IHBibWMuZGF0YSwgcHJvamVjdCA9ICJwYm1jM2siLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1pbi5jZWxscyA9IDMsIG1pbi5mZWF0dXJlcyA9IDIwMCkNCmBgYA0KDQoqTm90ZTogV2UgYXBwbHkgc29tZSBwcmVsaW1pbmFyeSBmaWx0ZXJzIGFzIHdlIGtub3cgdGhhdCBHRU1zIHdoaWNoIHZlcnkgbG93IG51bWJlcnMgb2YgZ2VuZXMgb3IgZ2VuZXMgd2hpY2ggYXJlIGJhcmVseSBkZXRlY3RlZCBhcmUgdW5saWtlbHkgdG8gYmUgYWN0dWFsIGNlbGxzLioNCg0KIyMjICoqMi4zLiBDaGVjayB0aGF0IGV2ZXJ5dGhpbmcgd2FzIGxvYWRlZCBjb3JyZWN0bHkqKg0KDQpgYGB7cn0NCnByaW50KHNldXJhdF9vYmplY3QpDQpgYGANCg0KIyMgMy4gT3ZlcnZpZXcgb2YgU2luZ2xlLWNlbGwgRGF0YSBTdHJ1Y3R1cmUNCg0KVG8gYmV0dGVyIHVuZGVyc3RhbmQgaG93IHRvIHdvcmsgd2l0aCBzaW5nbGUtY2VsbC9udWNsZWkgUk5BIHNlcXVlbmNpbmcgZGF0YSB3ZSB3aWxsIHRha2UgYSBkZWVwIGRpdmUgaW50byBob3cgZGF0YXNldHMgYXJlICp0eXBpY2FsbHkqIHN0cnVjdHVyZWQuIEluIHRoZSBkaWFncmFtIGJlbG93IHdlIGlsbHVzdHJhdGUgaG93IGRyb3BsZXQtYmFzZWQgc2NSTkFzZXEgYXBwcm9hY2hlcyBzZXBhcmF0ZSBjZWxscyBhbmQgdGhlaXIgbVJOQXMuIEJ1dCBob3cgZG9lcyB0aGlzIHRyYW5zbGF0ZSBpbnRvIFsqKipkYXRhKioqXXsudW5kZXJsaW5lfT8NCg0KIVtHZW5lcmF0ZWQgdXNpbmcgYmlvcmVuZGVyLmNvbV0oZmlndXJlcy9jZWxsR0VNcy5wbmcpe3N0eWxlPSJib3JkZXI6MXB4IHNvbGlkICMwMDAwMDA7IHBhZGRpbmc6M3B4OyBtYXJnaW46MnB4IiB3aWR0aD0iNDY5In0NCg0KIyMjIDMuMS4gVGhlIEdlbmUgeCBDZWxsIE1hdHJpeA0KDQpgYGB7cn0NCiMgUmF3IGNvdW50cyBpbiBhIHNldXJhdCBvYmplY3QgaXMgc2F2ZWQgaW4gdGhlIGFzc2F5cy1STkEtY291bnRzIHNsb3QgDQpyYXdfY291bnRzID0gYXMubWF0cml4KHNldXJhdF9vYmplY3RAYXNzYXlzJFJOQUBsYXllcnMkY291bnRzKQ0Kcm93bmFtZXMocmF3X2NvdW50cykgPSByb3duYW1lcyhzZXVyYXRfb2JqZWN0KQ0KY29sbmFtZXMocmF3X2NvdW50cykgPSBjb2xuYW1lcyhzZXVyYXRfb2JqZWN0KQ0KDQojIExldHMgbG9vayBhdCB0aGUgZmlyc3QgNCBjb2x1bW5zIGFuZCByb3dzDQpwcmludChyYXdfY291bnRzWzE6NCwxOjRdKQ0KYGBgDQoNCioqKldoYXQgZG9lcyB0aGlzIG1lYW4/KioqDQoNCioqQ29sdW1uczoqKiBUaGUgY29sdW1ucyByZXByZXNlbnQgYSB1bmlxdWUgYGJhcmNvZGVgLiBBcyB3ZSBjYW4gc2VlIGJlbG93LCB0aGlzIGBiYXJjb2RlYCBtYXJrcyBhbiBpbmRpdmlkdWFsIGNlbGwgd2hpY2ggaXMgaG93IHdlIGNhbiBpZGVudGlmeSBlYWNoIG9uZS4gU28gaW4gdGhpcyBvYmplY3QsIHRoZSBjb2x1bW5zIGFyZSB0aGUgY2VsbHMNCg0KIVtHZW5lcmF0ZWQgaW4gYmlvcmVuZGVyLmNvbSBhbmQgYWRhcHRlZCBmcm9tIERhbmllbHNraSwgSy4gKDIwMjMpLiBHdWlkYW5jZSBvbiBQcm9jZXNzaW5nIHRoZSAxMHggR2Vub21pY3MgU2luZ2xlIENlbGwgR2VuZSBFeHByZXNzaW9uIEFzc2F5LiBJbjogQ2Fsb2dlcm8sIFIuQS4sIEJlbmVzLCBWLiAoZWRzKSBTaW5nbGUgQ2VsbCBUcmFuc2NyaXB0b21pY3MuIE1ldGhvZHMgaW4gTW9sZWN1bGFyIEJpb2xvZ3ksIHZvbCAyNTg0LiBIdW1hbmEsIE5ldyBZb3JrLCBOWS4gPGh0dHBzOi8vZG9pLm9yZy8xMC4xMDA3Lzk3OC0xLTA3MTYtMjc1Ni0zXzE+XShmaWd1cmVzL1JlYWRPdmVydmlldy5wbmcpe3N0eWxlPSJib3JkZXI6MXB4IHNvbGlkICMwMDAwMDA7IHBhZGRpbmc6M3B4OyBtYXJnaW46MnB4IiB3aWR0aD0iODM0In0NCg0KKipSb3dzOioqIFdlIHNlZSB0aGF0IHRoZSBmaXJzdCBmZXcgcm93cyBoYXZlIG5hbWVzIHN1Y2ggYXMgKlJQMTEtMjA2TDEwLjkuKiBBIHF1aWNrICpnb29nbGUqIHNlYXJjaCB3aWxsIHRlbGwgeW91IHRoYXQgdGhpcyBpcyBhIGh1bWFuIGdlbmUgKEVuc2VtYmwgSUQgLSBFTlNHMDAwMDAyMzc0OTEpLiBTbyB3ZSBzZWUgaW4gdGhpcyBvYmplY3QgdGhhdCB0aGUgcm93cyByZXByZXNlbnQgdGhlIGdlbmVzLg0KDQojIyMgMy4yLiBDb3VudGluZyB0aGUgbnVtYmVyIG9mIGNlbGxzIGFuZCBnZW5lcw0KDQpTaW5nbGUtY2VsbCB0cmFuc2NyaXB0b21pYyBhbmFseXNpcyB0b29scyAoKmUuZy4qLCBTZXVyYXQpIGRvZXMgYSBsb3Qgb2YgdGhlIGhlYXZ5IGxpZnRpbmcgZm9yIHVzIHdoZW4gaXQgY29tZXMgdG8gY291bnRzIGFuZCBvdGhlciBzdW1tYXJ5IGRhdGEgaW5mb3JtYXRpb24uIEZvciBleGFtcGxlLCB0aGUgZm9sbG93aW5nIGNvZGUgd2lsbCB0ZWxsIHlvdSB0aGUgbnVtYmVyIG9mIGdlbmVzIGFuZCBjZWxscywgYXMgd2VsbCBzdG9yZSByYXcsIG5vcm1hbGl6ZWQsIGFuZCBzY2FsZWQgZGF0YSwgZGltZW5zaW9uIHJlZHVjdGlvbnMsIGFuZCBvdGhlciBhbmFseXNlcyB3aGljaCBtYXkgaGF2ZSBiZWVuIHBlcmZvcm1lZC4NCg0KYGBge3J9DQpwcmludChzZXVyYXRfb2JqZWN0KQ0KYGBgDQoNClRoaXMgaW5kaWNhdGVzIHRoYXQgd2UgaGF2ZSAxMyw3MTQgZ2VuZXMgKGZlYXR1cmVzKSBhY3Jvc3MgMiw3MDAgc2FtcGxlcy4NCg0KSG93ZXZlciwgd2UgY2FuIGFsc28gZXh0cmFjdCB0aGF0IGluZm9ybWF0aW9uIGZyb20gdGhlIGNvdW50IHRhYmxlOg0KDQpgYGB7cn0NCnByaW50KHBhc3RlMCgnQ2VsbHM6ICcsIG5jb2wocmF3X2NvdW50cykpKQ0KcHJpbnQocGFzdGUwKCdGZWF0dXJlczonLCBucm93KHJhd19jb3VudHMpKSkNCmBgYA0KDQpUaGVzZSBudW1iZXJzIHNob3VsZCBtYXRjaCENCg0KIyMjIDMuMy4gQSBkZWVwZXIgZGl2ZSBpbnRvIGNvdW50cw0KDQojIyMjIDMuM2EuIFdoYXQgaXMgc2VxdWVuY2luZyBkZXB0aD8NCg0KYGBge3J9DQojIENhbGN1bGF0ZSB0aGUgdG90YWwgcmVhZHMgaW4gZWFjaCBpbmRpdmlkdWFsIGNlbGwNCnJlYWRfZGVwdGhzID0gY29sU3VtcyhyYXdfY291bnRzKQ0KDQojIEhvdyBhcmUgdGhleSBkaXN0cmlidXRlZD8NCmhpc3QocmVhZF9kZXB0aHMsIG1haW4gPSAiSGlzdG9ncmFtIG9mIHJlYWQgZGVwdGgiLCANCiAgICAgeGxhYiA9ICJEZXB0aCIsIHlsYWIgPSAiRnJlcXVlbmN5IiwNCiAgICAgYnJlYWtzID0gMjAwKQ0KYGBgDQoNClRoaXMgaGlzdG9ncmFtIHNob3dzIHRoYXQgdGhlIG51bWJlciBvZiBtUk5BIHRyYW5zY3JpcHRzIGRldGVjdGVkIGluIGVhY2ggY2VsbCByYW5nZWQgZnJvbSA1NDYgdG8gMTUsODE4LCBidXQgdGhhdCBtb3N0IHdlcmUgYXJvdW5kIDIsMDAwLiBTbyB3aGF0IG1pZ2h0IGFjY291bnQgZm9yIHRoZXNlIGRpZmZlcmVuY2VzPw0KDQotICAgKipDZWxsIHR5cGVzOioqIFNvbWUgY2VsbCB0eXBlcyBtYXkgc2ltcGx5IGp1c3QgZXhwcmVzcyBtb3JlIG1STkEgdGhhbiBvdGhlcnMuIEZvciBleGFtcGxlLCBIZXBhdG9jeXRlcyBhcmUga25vd24gdG8gY29udGFpbiBhIGxvdCBvZiBSTkEgd2hpbGUgb3RoZXJzIG1heSBub3QuDQoNCi0gICAqKlN0b2NoYXN0aWMgZ2VuZSBleHByZXNzaW9uOioqIEZsdWN0dWF0aW9ucyBpbiB0cmFuc2NyaXB0aW9uIGFuZCBkZWdyYWRhdGlvbiBmcm9tIG5vcm1hbCBiaW9sb2dpY2FsIHByb2Nlc3NlcywgY2VsbCBzdGF0ZXMsIGFuZCBvdGhlciBmYWN0b3JzIGluIGluZGl2aWR1YWwgY2VsbHMgd2hpY2ggaGF2ZSB2ZXJ5IGxvdyBhbW91bnRzIG9mIFJOQSB0byBiZWdpbiBjYW4gYXBwZWFyIGFzIGJpZyBkaWZmZXJlbmNlcy4NCg0KLSAgICoqVGVjaG5pY2FsIHZhcmlhYmlsaXR5OioqIFNlcXVlbmNpbmcgZGVwdGgsIGFzc2F5IGtpdHMsIGFuZCBvdGhlciB0ZWNobmljYWwgZmFjdG9ycyBtYXkgY29udHJpYnV0ZSB0byB0aGVzZSBkaWZmZXJlbmNlcy4NCg0KWypUaGUgemVyby1pbmZsYXRpb24gZGViYXRlKl17LnVuZGVybGluZX0qOiBJbiB0aGUgZWFybHkgZGF5cyBvZiBzY1JOQXNlcSBpdCB3YXMgYSB3aWRlbHkgYWNjZXB0ZWQgdGhhdCBkYXRhIHdhcyB6ZXJvLWluZmxhdGVkLCBpLmUuLCBpdCBoYWQgbW9yZSB6ZXJvJ3MgdGhhbiBpdCBzaG91bGQgYmVjYXVzZSBvZiB0ZWNobmljYWwgY2hhbGxlbmdlcy4gVG9kYXksIHdlIGJldHRlciB1bmRlcnN0YW5kIHRoYXQgdGhlIGxhcmdlIG51bWJlciBvZiB6ZXJvJ3MgYXJlIG5vdCBvbmx5IHRlY2huaWNhbCwgYnV0IGluIGZhY3QgZG9lcyByZWZsZWN0IHRydWUgYmlvbG9neSBhcyB3ZWxsLiBUaGUgZm9sbG93aW5nIG1hbnVzY3JpcHQgZGVsdmVzIGRlZXBlciBpbnRvIHRoZSBkZWJhdGU6IFtKaWFuZyBldCBhbC4sIDIwMjJdKGh0dHBzOi8vZ2Vub21lYmlvbG9neS5iaW9tZWRjZW50cmFsLmNvbS9hcnRpY2xlcy8xMC4xMTg2L3MxMzA1OS0wMjItMDI2MDEtNSkqDQoNCiMjIyMgMy4zYi4gQW5vdGhlciB3YXkgdG8gbG9vayBhdCBkZXB0aA0KDQpTZXF1ZW5jaW5nIGRlcHRoIHVsdGltYXRlbHkgZHJpdmVzIHRoZSBhYmlsaXR5IHRvIGRldGVjdCBpbmRpdmlkdWFsIGdlbmVzIGluIGEgY2VsbCBvciBzYW1wbGUgKGJ1bGsgUk5BIHNlcSkuIEFzIG1vcmUgcmVhZHMgYXJlIHBlcmZvcm1lZCBvbiBhIHNhbXBsZSwgdGhlIHByb2JhYmlsaXR5IG9mIGNhcHR1cmluZyBhIGxvd2VyIGFidW5kYW5jZSBnZW5lIGluY3JlYXNlcy4gVGhlcmVmb3JlLCBzZXF1ZW5jaW5nIGRlcHRoIGNhbiBoYXZlIGEgc2lnbmlmaWNhbnQgaW1wYWN0IG9uIHRoZSBlbmQgcmVzdWx0cyBvZiB5b3VyIHNlcXVlbmNpbmcgcmVzdWx0cy4NCg0KKlNvIHdoeSBub3Qgc2VxdWVuY2UgYXMgZGVlcCBhcyBwb3NzaWJlPyogQXMgd2l0aCBhbnl0aGluZywgd2UgbmVlZCB0byBiYWxhbmNlIGNvc3Qgd2l0aCB2YWx1ZS4gVGhlIGZvbGxvd2luZyBmaWd1cmVzIGFyZSBzdGFuZGFyZCBvdXRwdXRzIGZyb20gc2NSTkFzZXEgcmVwb3J0cyBpbmNsdWRpbmcgdGhvc2UgbGlua2VkIGFib3ZlLg0KDQohW10oaW1hZ2VzL1VudGl0bGVkJTIwKDgpLnBuZyl7c3R5bGU9ImJvcmRlcjoxcHggc29saWQgIzAwMDAwMDsgcGFkZGluZzozcHg7IG1hcmdpbjoycHgiIHdpZHRoPSI3MDgifQ0KDQpBcyB5b3UgY2FuIHNlZSwgdGhlIG1vcmUgcmVhZHMgd2UgaGF2ZSBwZXIgY2VsbCwgdGhlIHNtYWxsZXIgdGhlIHNsb3AgZm9yIGNhcHR1cmluZyBuZXcgZ2VuZXMgZ2V0cy4gVGhlcmVmb3JlLCB0aGUgZGlmZmVyZW5jZSBiZXR3ZWVuIGNhcHR1cmluZyA4MCUgb2YgZ2VuZXMgYW5kIDEwMCUgY291bGQgbWVhbiAyLCAxMCwgb3IgZXZlbiBcPjEwMFggdGhlIGFtb3VudCBvZiBzZXF1ZW5jaW5nLiBJcyBpdCB0cnVseSB3b3J0aCBpdCBmb3IgY2FwdHVyaW5nIGEgZ2VuZSB3aG8gZXhwcmVzc2lvbiBpcyBzbyBsb3c/IFRoaXMgaXMgd2h5IHdlIGxpbWl0IG91ciBkZXB0aCBmb3Igc2VxdWVuY2luZyBhbmQgd2h5IG1vc3QgZGF0YXNldHMgcHJvZHVjZWQgd2l0aCB0aGUgc2FtcGxlIHBsYXRmb3JtIHdpbGwgdGVuZCB0byBkZXRlY3QgYSBzaW1pbGFyIG51bWJlciBvZiBnZW5lcy4NCg0KIyMjIDMuNC4gQ29tbW9uIFN0b3JhZ2UgRm9ybWF0cw0KDQojIyMjIDMuNGEuIFNwYXJzZSB0YWJsZXMgKHJhdykNCg0KV2UgcHJldmlvdXNseSBtZW50aW9uZWQgdGhhdCBzaW5nbGUtY2VsbC9udWNsZWkgdHJhbnNjcmlwdG9taWMgZGF0YSBoYXMgYSBsb3Qgb2YgemVyb3MuIExldHMgYWN0dWFsbHkgbG9vayBhdCB0aGUgY291bnQgZGlzdHJpYnV0aW9uIHNvIHlvdSBjYW4gc2VlOg0KDQpgYGB7cn0NCiMgV2Ugd2FuIHRvIGNvbGxhcHNlIGFsbCB0aGUgdmFsdWVzIGludG8gYSB2ZWN0b3Igb2YgdmFsdWVzDQptYXRfdmFsdWVzIDwtIGFzLnZlY3RvcihyYXdfY291bnRzKQ0KDQojIFBsb3QgaGlzdG9ncmFtDQpoaXN0KG1hdF92YWx1ZXMsIG1haW4gPSAiSGlzdG9ncmFtIG9mIE1hdHJpeCBWYWx1ZXMiLCB4bGFiID0gIlZhbHVlIiwgeWxhYiA9ICJGcmVxdWVuY3kiLCBicmVha3MgPSA2MDAsIHhsaW0gPSBjKDAsMTApKQ0KYGBgDQoNClRoaXMgaXMgd2h5IHlvdSB3aWxsIGFsbW9zdCBhbHdheXMgZmluZCByYXcgc2NSTkFzZXEgZGF0YSBzYXZlZCBhcyBhIGAubXR4YCAoc3BhcnNlIG1hdHJpeCkgZmlsZSBvciBmb3JtYXQuDQoNCkhlcmUgd2Ugd2lsbCBkb3dubG9hZCBwdWJsaWNseSBhY2Nlc3NpYmxlIHNwYXJzZSBtYXRyaWNlcyBmcm9tIHRoZSBbR2VuZSBFeHByZXNzaW9uIE9tbmlidXNdKGh0dHBzOi8vd3d3Lm5jYmkubmxtLm5paC5nb3YvZ2VvKSwgYW4gZXhjZWxsZW50IHJlc291cmNlIGZvciBvYnRhaW5pbmcgcmF3IGRhdGEgaWYgeW91IHdhbnQgdG8gcmV1c2UgYW5kIHJlYW5hbHl6ZSBleGlzdGluZyBkYXRhLiBUaGlzIGRhdGFzZXQgaXMgZnJvbSBbc2luZ2xlLW51Y2xlaSBSTkEgc2VxdWVuY2luZyBvZiBsaXZlcnMgZnJvbSBtaWNlIHRyZWF0ZWQgd2l0aCB0aGUgZW52aXJvbm1lbnRhbCBjb250YW1pbmFudCAyLDMsNyw4LXRldHJhY2hsb3JvZGliZW56by0qcCotZGlveGluIChUQ0REKV0oaHR0cHM6Ly93d3cubmNiaS5ubG0ubmloLmdvdi9nZW8vcXVlcnkvYWNjLmNnaT9hY2M9R1NFMTg0NTA2KS4NCg0KYGBge3J9DQojZG93bmxvYWQuZmlsZSgnaHR0cHM6Ly93d3cubmNiaS5ubG0ubmloLmdvdi9nZW8vZG93bmxvYWQvP2FjYz1HU0UxODQ1MDYmZm9ybWF0PWZpbGUmZmlsZT1HU0UxODQ1MDYlNUZzY3QlMkVtdHglMkVneicsICcuL2Rvd25sb2Fkcy9tYXRyaXgubXR4Lmd6JykNCnN5c3RlbSgiZ3VuemlwIC4vZG93bmxvYWRzL21hdHJpeC5tdHguZ3oiKQ0KDQojZG93bmxvYWQuZmlsZSgnaHR0cHM6Ly93d3cubmNiaS5ubG0ubmloLmdvdi9nZW8vZG93bmxvYWQvP2FjYz1HU0UxODQ1MDYmZm9ybWF0PWZpbGUmZmlsZT1HU0UxODQ1MDYlNUZzY3QlMkViYXJjb2RlcyUyRXRzdiUyRWd6JywgJy4vZG93bmxvYWRzL2JhcmNvZGVzLnRzdi5neicpDQpzeXN0ZW0oImd1bnppcCAuL2Rvd25sb2Fkcy9iYXJjb2Rlcy50c3YuZ3oiKQ0KDQojZG93bmxvYWQuZmlsZSgnaHR0cHM6Ly93d3cubmNiaS5ubG0ubmloLmdvdi9nZW8vZG93bmxvYWQvP2FjYz1HU0UxODQ1MDYmZm9ybWF0PWZpbGUmZmlsZT1HU0UxODQ1MDYlNUZzY3QlMkVnZW5lcyUyRXRzdiUyRWd6JywgJy4vZG93bmxvYWRzL2dlbmVzLnRzdi5neicpDQpzeXN0ZW0oImd1bnppcCAuL2Rvd25sb2Fkcy9nZW5lcy50c3YuZ3oiKQ0KYGBgDQoNCmBgYHtyfQ0KIyBSZWFkIHRoZSBmaXJzdCBmZXcgbGluZXMgb2YgdGhlIGZpbGUNCmxpbmVzIDwtIHJlYWRMaW5lcygnLi9kb3dubG9hZHMvbWF0cml4Lm10eCcsIG4gPSAxMCkNCg0KIyBQcmludCB0aGUgbGluZXMNCmNhdChsaW5lcywgc2VwID0gIlxuIikNCmBgYA0KDQpBcyB5b3UgY2FuIHNlZSwgdGhlcmUgaXMgbm90IGEgemVybyBpbiBzaWdodCBoZXJlLiBBZnRlciB5b3UgcmVhZCB0aGUgZmlyc3QgMiBzdW1tYXJ5IGxpbmVzIHlvdSBjYW4gc2VlIHRoZSBmb2xsb3dpbmcgc3RydWN0dXJlOg0KDQp8IEZlYXR1cmUvR2VuZSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBCYXJjb2RlL0NlbGwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgQ291bnQvVmFsdWUgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tLS0tLS0tLXwNCnwgUmVmZXJzIHRvIHRoZSByb3cgbnVtYmVyIGluIHRoZSBnZW5lcy50c3YgZmlsZSB8IFJlZmVycyB0byB0aGUgcm93IG51bWJlciBpbiB0aGUgYmFyY29kZXMudHN2IGZpbGUgfCBJbmNsdWRlIGFsbCBub24temVybyB2YWx1ZXMgb2YgdGhlIGRhdGFzZXQgfA0KDQpMZXQncyBzZWUgd2hhdCB0aGlzIGlzIGhlcmUNCg0KYGBge3J9DQojIFJlYWQgdGhlIHNwZWNpZmljIGxpbmUgb2YgdGhlIGZpbGUNCnJlYWRMaW5lcygnLi9kb3dubG9hZHMvZ2VuZXMudHN2JywgbiA9IDE1KVsxNV0NCg0KIyBSZWFkIHRoZSBzcGVjaWZpYyBsaW5lIG9mIHRoZSBmaWxlDQpyZWFkTGluZXMoJy4vZG93bmxvYWRzL2JhcmNvZGVzLnRzdicsIG4gPSAxKVsxXQ0KYGBgDQoNCkZyb20gaGVyZSB3ZSBjYW4gc2VlIHRoYXQgdGhlIGZpcnN0IHJvdyBvZiBkYXRhIGluIHRoZSBgbXR4YCBmaWxlIHJlcHJlc2VudCB0aGUgZ2VuZSAqKipTbnRnMSoqKiBmb3IgdGhlIGNlbGwgd2l0aCBiYXJjb2RlICoqKkFBQUNDQ0FDQUFDVFRDVFRfMSoqKi4NCg0KIyMjIyAzLjRiLiBTZXVyYXQgb2JqZWN0DQoNClshW0ltYWdlIGZyb20gaHR0cHM6Ly9zaWItc3dpc3MuZ2l0aHViLmlvL3NpbmdsZS1jZWxsLXRyYWluaW5nL2RheTEvZGF5MS0yX2FuYWx5c2lzX3Rvb2xzX3FjLmh0bWxdKGZpZ3VyZXMvc2V1cmF0X29iamVjdC5wbmcpe3N0eWxlPSJib3JkZXI6MXB4IHNvbGlkICMwMDAwMDA7IHBhZGRpbmc6M3B4OyBtYXJnaW46MnB4IiB3aWR0aD0iNjEzIn1dKGh0dHBzOi8vc2liLXN3aXNzLmdpdGh1Yi5pby9zaW5nbGUtY2VsbC10cmFpbmluZy9kYXkxL2RheTEtMl9hbmFseXNpc190b29sc19xYy5odG1sKQ0KDQpTZXVyYXQgb2JqZWN0cyBhcmUgcHJvYmFibHkgdGhlIG1vc3QgY29tbW9ubHkgdXNlZCB0byBzdG9yZSBhbmQgb3JnYW5pemUgc2NSTkFzZXEgZGF0YSBmb3IgUiB1c2Vycy4gU28gZmFyIHRoaXMgaXMgd2hhdCB3ZSBoYXZlIHVzZWQgZm9yIHRoZSBwdWJsaXNoZWQgUEJNQyBkYXRhIHdoaWNoIHdlIGltcG9ydGVkIGluICpTZWN0aW9uIDIqLiBMZXQncyB0YWtlIGFub3RoZXIgY2xvc2VyIGxvb2sgYXQgdGhpcyBvYmplY3Q6DQoNCmBgYHtyfQ0KcHJpbnQoc2xvdE5hbWVzKHNldXJhdF9vYmplY3QpKSAjIExldHMgZXhhbWluZSBhbGwgdGhlIHNsb3RzDQpgYGANCg0KVGhlcmUgaXMgYSBsb3Qgb2YgaW5mb3JtYXRpb24sIGJ1dCB0aGUgb25lcyB5b3Ugd2lsbCBtb3N0IGNvbW1vbmx5IHVzZSBpZiB5b3UgZG8gc2luZ2xlLWNlbGwgYW5hbHlzZXMgaW4gUiBhcmUgYGFzc2F5c2AsIGBtZXRhLmRhdGFgLCBhbmQgYHJlZHVjdGlvbnNgDQoNCi0gICAqKipBc3NheXMqKiogd2lsbCBob2xkIHRoZSBjb3VudCBkYXRhDQoNCi0gICAqKipNZXRhLmRhdGEqKiogd2lsbCBob2xkIHRoZSBtZXRhZGF0YSBmb3IgZWFjaCBpbmRpdmlkdWFsIGNlbGwvbnVjbGVpDQoNCi0gICAqKipSZWR1Y3Rpb25zKioqIHdpbGwgaG9sZCB0aGUgZGltZW5zaW9uIHJlZHVjdGlvbnMgZm9yIHZpc3VhbGl6YXRpb25zDQoNCiMjIyMgMy40Yy4gU2luZ2xlQ2VsbEV4cGVyaW1lbnQNCg0KQW5vdGhlciBjb21tb24gZm9ybWF0IGluIFIgYXJlIFNpbmdsZUNlbGxFeHBlcmltZW50cy4gVGhleSBjb250YWluIHRoZSBzYW1lIGluZm9ybWF0aW9uLCBidXQgYXJlIG9yZ2FuaXplZCBpbiBhIGRpZmZlcmVudCBtYW5uZXIuDQoNClshW2ltYWdlIGZyb20gaHR0cHM6Ly9iaW9jb25kdWN0b3Iub3JnL2Jvb2tzLzMuMTMvT1NDQS5pbnRyby90aGUtc2luZ2xlY2VsbGV4cGVyaW1lbnQtY2xhc3MuaHRtbF0oZmlndXJlcy9TaW5nbGVDZWxsRXhwZXJpbWVudC5wbmcpe3N0eWxlPSJib3JkZXI6MXB4IHNvbGlkICMwMDAwMDA7IHBhZGRpbmc6M3B4OyBtYXJnaW46MnB4IiB3aWR0aD0iNjE4In1dKGh0dHBzOi8vYmlvY29uZHVjdG9yLm9yZy9ib29rcy8zLjEzL09TQ0EuaW50cm8vdGhlLXNpbmdsZWNlbGxleHBlcmltZW50LWNsYXNzLmh0bWwpDQoNCkZpcnN0LCBsZXQncyBjb252ZXJ0IG91ciBzZXVyYXQgb2JqZWN0IHRvIFNpbmdsZUNlbGxFeHBlcmltZW50LiBNb3N0IGZvcm1hdHMgY2FuIGVhc2lseSBiZSBpbnRlcmNoYW5nZWQ6DQoNCmBgYHtyfQ0Kc2NlID0gYXMuU2luZ2xlQ2VsbEV4cGVyaW1lbnQoc2V1cmF0X29iamVjdCkNCmBgYA0KDQpOZXh0LCBsZXRzIHNlZSB3aGF0IHRoZSBzdHJ1Y3R1cmUgbG9va3MgbGlrZToNCg0KYGBge3J9DQpwcmludChzY2UpDQpgYGANCg0KSGVyZSB3ZSBjYW4gc2VlOg0KDQotICAgKipyb3duYW1lcyoqIGFuZCByb3dEYXRhIGNvbnRhaW4gdGhlIGdlbmUgaW5mb3JtYXRpb24gYW5kIG1ldGFkYXRhDQoNCi0gICAqKmNvbG5hbWVzKiogYW5kIGNvbERhdGEgY29udGFpbiB0aGUgY2VsbCBpbmZvcm1hdGlvbiBhbmQgbWV0YWRhdGENCg0KLSAgICoqYXNzYXlzKiogaGVyZSBhbHNvIGhvbGQgdGhlIGNvdW50IGRhdGENCg0KV2UgY2FuIGV4YW1pbmUgdGhlIHBpZWNlcyBvZiBkYXRhIHN1Y2ggYXMgdGhlIGNlbGwgbWV0YWRhdGEgYXMgZm9sbG93czoNCg0KYGBge3J9DQpwcmludChjb2xEYXRhKHNjZSkpDQpgYGANCg0KIyMjIyAzLjRkLiBBbm5EYXRhL0g1QUQgKFB5dGhvbikNCg0KVGhpcyB3b3Jrc2hvcCB3aWxsIG5vdCBnbyBpbnRvIHB5dGhvbiByZXNvdXJjZXMsIGJ1dCBpdCBpcyBpbXBvcmFudCB0byBub3RlIHRoYXQgUHl0aG9uIGFuZCBSIGFyZSB0aGUgdHdvIG1vc3QgY29tbW9uIHByb2dyYW1taW5nIGxhbmd1YWdlcyBmb3Igc2luZ2xlLWNlbGwgdHJhbnNjcmlwdG9taWMgYW5hbHlzaXMgYW5kIGl0IGlzIG5vdCB1bmNvbW1vbiB0byBoYXZlIHRvIHVzZSBib3RoLiBUaGUgSDVhZCBmaWxlIGZvcm1hdCBpcyBzaW1pbGFyIHRvIHRoZSBTZXVyYXQgb2JqZWN0IGFuZCBTaW5nbGVDZWxsRXhwZXJpbWVudCBvYmplY3QgaW4gdGhlIHNlbnNlIHRoYXQgaXQgc3RvcmVzIHRoZSBkaWZmZXJlbnQgY29tcG9uZW50cyBvZiB0aGUgZGF0YSBpbiBhbiBvcmdhbml6ZWQgbWFubmVyLg0KDQpbIVtJbWFnZSBmcm9tOiBodHRwczovL3d3dy5zYy1iZXN0LXByYWN0aWNlcy5vcmcvaW50cm9kdWN0aW9uL2FuYWx5c2lzX3Rvb2xzLmh0bWxdKGZpZ3VyZXMvYW5uZGF0YS5qcGcpe3N0eWxlPSJib3JkZXI6MXB4IHNvbGlkICMwMDAwMDA7IHBhZGRpbmc6M3B4OyBtYXJnaW46MnB4IiB3aWR0aD0iNDMwIn1dKGh0dHBzOi8vd3d3LnNjLWJlc3QtcHJhY3RpY2VzLm9yZy9pbnRyb2R1Y3Rpb24vYW5hbHlzaXNfdG9vbHMuaHRtbCkNCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIDQuIEFzc2Vzc2luZyBkYXRhIHF1YWxpdHkNCg0KQW55IGFuYWx5c2lzLCBubyBtYXR0ZXIgb2Ygc2l6ZSwgc2hvdWxkIHVuZGVyZ28gcmlnb3JvdXMgUUMuIFdoZW4gaXQgY29tZXMgdG8gc2luZ2xlLWNlbGwgdHJhbnNjcmlwdG9taWMgZGF0YSwgdGhlcmUgYXJlIHNldmVyYWwgY29tcHV0YXRpb25hbCB0b29scyB0aGF0IGhlbHAgeW91IGluIHRoZSBwcm9jZXNzIGZyb20gZW5hYmxpbmcgdmlzdWFsaXphdGlvbnMgdG8gcGVyZm9ybWluZyBhIHBhbmVsIG9mIFFDIHRlc3RzIGFuZCByZXBvcnRpbmcgc2V2ZXJhbCBkaWZmZXJlbnQgbWV0cmljcyBmb3IgdGhlIGVuZCB1c2VyIHRvIGV2YWx1YXRlLiBBIGxpc3Qgb2YganVzdCBzb21lIG9mIHRoZXNlIHRvb2xzIGNhbiBiZSBmb3VuZCBbaGVyZV0oaHR0cHM6Ly93d3cuc2NybmEtdG9vbHMub3JnL3Rvb2xzP3NvcnQ9bmFtZSZjYXRzPVF1YWxpdHlDb250cm9sKS4NCg0KTGV0J3MgZ28gdGhyb3VnaCBzb21lIG9mIHRoZSBtb3N0IGNvbW1vbiBRQyBtZXRyaWNzLCB3aGF0IHRvIHRoZXkgbWVhbiwgYW5kIHdoeSBkbyB0aGV5IG1hdHRlcj8NCg0KIyMjIDQuMS4gR2VuZXMgYW5kIHRyYW5zY3JpcHRzDQoNCipIb3cgZG9lcyB0aGUgbnVtYmVyIG9mIGRldGVjdGVkIGdlbmVzIGFuZCB0cmFuc2NyaXB0cyBpbmRpY2F0ZSBxdWFsaXR5PyogV2hpbGUgd2UgZG9uJ3QgaGF2ZSBhIGtub3duIG51bWJlciBvZiBleHByZXNzZWQgZ2VuZXMgZm9yIGVhY2ggY2VsbCB0eXBlLCB3ZSBkbyBrbm93IHRoYXQgY2VsbHMgcmVxdWlyZSB0aGUgZXhwcmVzc2lvbiBvZiBodW5kcmVkcyB0byB0aG91c2FuZHMgb2YgZ2VuZXMgdG8gZnVuY3Rpb24gcHJvcGVybHkuDQoNCioqTnVtYmVyIG9mIGdlbmVzIChmZWF0dXJlcykqKg0KDQpXZSBjYW4gY2hlY2sgdGhlc2UgdmFsdWVzIGJ5IHJ1bm5pbmcgdGhlIGNvZGUgYmVsb3c6DQoNCmBgYHtyfQ0KVmxuUGxvdChzZXVyYXRfb2JqZWN0LCBmZWF0dXJlcyA9ICduRmVhdHVyZV9STkEnKSArIE5vTGVnZW5kKCkNCmBgYA0KDQpIZXJlIHdlIGNhbiBzZWUgdGhhdCAoMSkgdGhlIG51bWJlciBvZiBnZW5lcyBkZXRlY3RlZCByYW5nZXMgZnJvbSBcfjIwMCAtIDMwMDAgYW5kICgyKSBtb3N0IGV4cHJlc3MgXH45MDAgZ2VuZXMuICpJcyB0aGlzIG5vcm1hbCBhbmQgZXhwZWN0ZWQ/KiBJbiByZWFsaXR5LCB0aGVyZSBpcyBubyB1bml2ZXJzYWwgdGhyZXNob2xkLCB0aGVzZSB2YWx1ZXMgd2lsbCBkZXBlbmQgb24gdGhlIGNlbGwgdHlwZSBhbmQgdGhlIHNlcXVlbmNpbmcgZGVwdGguIEJ1dCB3ZSBkbyBrbm93IHRoYXQgaWYgbW9zdCBjZWxscyBoYWQgZXh0ZW1lbHkgbG93IGNvdW50cyB0aGF0IGVpdGhlciBjZWxscyB3ZXJlIG5vdCBoZWFsdGh5L2ludGFjdCBvciBzZXF1ZW5jaW5nIGRlcHRoIHdhcyBtdWNoIHRvbyBsb3cuIEl0IGNvdWxkIGFsc28gcG9pbnQgdG8gZXJyb3JzIGZ1cnRoZXIgdXBzdHJlYW0gc3VjaCBhcyBhbGlnbm1lbnQgdG8gdGhlIGluY29ycmVjdCByZWZlcmVuY2UgZ2Vub21lLg0KDQoqKk51bWJlciBvZiBtUk5BIG1vbGVjdWxlcyAoY291bnQpKioNCg0KV2UgY2FuIGFsc28gZXhhbWluZSB0aGUgdG90YWwgbnVtYmVyIG9mIG1STkEgbW9sZWN1bGVzIGRldGVjdGVkOg0KDQpgYGB7cn0NClZsblBsb3Qoc2V1cmF0X29iamVjdCwgZmVhdHVyZXMgPSAnbkNvdW50X1JOQScpDQpgYGANCg0KQXMgYWJvdmUsIHdlIGNhbiBzZWUgYSBnb29kIGRpc3RyaWJ1dGlvbiBjZW50ZXJlZCBhcm91bmQgMjUwMCBtUk5BIG1vbGVjdWxlcy4gVGhlc2UgdmFsdWUgc2hvdWxkIGFsd2F5cyBiZSBoaWdoZXIgdGhhbiB0aGUgbnVtYmVyIG9mIGZlYXR1cmVzLg0KDQoqKkNvcnJlbGF0aW9uIG9mIGZlYXR1cmVzIGFuZCBtb2xlY3VsZXMqKg0KDQpBIHRoaXJkIHdheSB0byBsb29rIGF0IHRoaXMgZGF0YSBpcyB0byBleGFtaW5lIHRoZSBjb3JyZWxhdGlvbiBvZiBmZWF0dXJlcyBhbmQgbVJOQSBtb2xlY3VsZXM6DQoNCmBgYHtyfQ0KRmVhdHVyZVNjYXR0ZXIob2JqZWN0ID0gc2V1cmF0X29iamVjdCwgZmVhdHVyZTEgPSAnbkNvdW50X1JOQScsIGZlYXR1cmUyID0gJ25GZWF0dXJlX1JOQScpDQpgYGANCg0KVGhpcyBtZXRyaWMgaXMgbW9yZSBpbmZvcm1hdGl2ZSBiZWNhdXNlIGl0IHRlbGxzIHlvdSB0aGF0IGFzIHlvdSBkZXRlY3QgbW9yZSBtUk5BIG1vbGVjdWxlcywgeW91IGFsc28gZGV0ZWN0IG1vcmUgZGlmZmVyZW50IGdlbmVzLiBUaGlzIHdvdWxkIGdlbmVyYWxseSBiZSBleHBlY3RlZCBhcyB5b3UgYXJlIGdldHRpbmcgYSBsYXJnZXIgc2FtcGxpbmcgb2YgdGhlIG1STkEgcmVzdWx0aW5nIGluIGFuIGluY3JlYXNlZCBjaGFuY2Ugb2Ygc2FtcGxpbmcgZGlmZmVyZW50IGdlbmVzLiBIb3dldmVyLCB3aGVuIHlvdSBhc3Nlc3MgdGhlc2UgcGxvdHMsIHlvdSB3aWxsIHdhbnQgdG8gWyoqa2VlcCB0aGUgYmlvbG9neSBpbiBtaW5kKipdey51bmRlcmxpbmV9Lg0KDQojIyMgNC4yLiBNaXRvY2hvbmRyaWFsIFJOQQ0KDQpIZXJlLCBiaW9sb2dpY2FsIGtub3dsZWRnZSBpcyBleHRyZW1lbHkgaW1wb3J0YW50IGZvciBldmFsdWF0aW5nIHRoaXMgbWV0cmljIGFuZCByZW1vdmluZyBjZWxscyBiYXNlZCBvbiBhIHRocmVzaG9sZC4gQ29uc2lkZXIgdGhlc2UgdGhyZWUgZXhwZXJpbWVudHM6DQoNCi0gICBOdWNsZWkgaXNvbGF0ZWQgZnJvbSBtdXNjbGUgY2VsbHMgd2hpY2ggcmVxdWlyZSBhIGxvdCBvZiBlbmVyZ3kNCg0KLSAgIE11c2NsZSB3aG9sZSBjZWxscyB3aGljaCByZXF1aXJlIGEgbG90IG9mIGVuZXJneQ0KDQotICAgU2tpbiBjZWxscyB3aGljaCBhcmUgbGVzcyBtZXRhYm9saWNhbGx5IGFjdGl2ZQ0KDQoqV2hhdCB3b3VsZCB5b3UgZXhwZWN0IHRvIHNlZSAocXVhbGl0YXRpdmUpIGluIHRoZSBhbW91bnQgb2YgbWl0b2Nob25kcmlhbCBnZW5lcyBmb3IgZWFjaCBvZiB0aGVzZSBleHBlcmltZW50cz8qDQoNCioqQXNzZXNzaW5nIHBlcmNlbnQgbWl0b2Nob25kcmlhIGNvdW50cyoqDQoNCldlIGNhbiBhc3Nlc3MgdGhlIHBlcmNlbnRhZ2Ugb2YgbWl0b2Nob25kcmlhbCBnZW5lcyBieSBjYWxjdWxhdGluZyB0aGUgbnVtYmVyIG9mIGNvdW50cyBjb21pbmcgZnJvbSBtaXRvY2hvbmRyaWFsIGdlbmVzICh1c3VhbGx5IHByZWZpeGVkIHdpdGggbXQtIG9yIE1ULSkgY29tcGFyZWQgdG8gdGhlIHRvdGFsIGNvdW50cy4gVGhpcyBjYW4gYmUgdmlzdWFsaXplZCBsaWtlIHRoaXM6DQoNCmBgYHtyfQ0KIyBDYWxjdWF0ZSB0aGUgcGVyY2VudGFnZSBvZiBtaXRvY2hvbmRyaWFsIGNvdW50cw0Kc2V1cmF0X29iamVjdFtbInBlcmNlbnQubXQiXV0gPC0gUGVyY2VudGFnZUZlYXR1cmVTZXQoc2V1cmF0X29iamVjdCwgcGF0dGVybiA9ICJeTVQtIikNCg0KIyBQbG90IHRoZSB2YWx1ZXMNClZsblBsb3Qoc2V1cmF0X29iamVjdCwgZmVhdHVyZXMgPSAncGVyY2VudC5tdCcpDQpgYGANCg0KV2Ugc2VlIHRoYXQgdGhlIHBlcmNlbnRhZ2Ugb2YgY291bnRzIHJlcHJlc2VudGluZyBtaXRvY2hvbmRyaWFsIGdlbmVzIGlzIG1vc3RseSBiZWxvdyA1JSBidXQgYXMgaGlnaCBhcyBcfjI1JS4gVG8gZXZhbHVhdGUgdGhlc2UgcmVzdWx0cyB0aGluayBiYWNrIHRvIHRoZSB0aHJlZSBleHBlcmltZW50cyBhbmQgY29uc2lkZXIgd2hhdCB5b3Ugd291bGQgZXhwZWN0IHRvIHNlZToNCg0KLSAgIE51Y2xlaSBpc29sYXRlZCBmcm9tIG11c2NsZSBjZWxscyB3aGljaCByZXF1aXJlIGEgbG90IG9mIGVuZXJneQ0KDQogICAgRGVzcGl0ZSBiZWluZyBtZXRhYm9saWNhbGx5IGFjdGl2ZSwgbnVjbGVpIGlzb2xhdGlvbiBpcyBleHBlY3RlZCB0byB3YXNoIG91dCBtaXRvY2hvbmRyaWEuIEhpZ2ggY29udGFtaW5hdGlvbiBpcyBpbmRpY2F0aXZlIG9mIGhpZ2ggYW1iaWVudCBSTkEgb3IgbWl0b2Nob25kcmlhIHN0aWNraW5nIHRvIHRoZSB0aGUgbnVjbGVpLg0KDQotICAgTXVzY2xlIHdob2xlIGNlbGxzIHdoaWNoIHJlcXVpcmUgYSBsb3Qgb2YgZW5lcmd5DQoNCiAgICBHaXZlbiB0aGF0IG11c2NsZSBjZWxscyBhcmUgaGlnaGx5IG1ldGFib2xpY2FsbHkgYWN0aXZlIGFuZCBhcmUga25vd24gdG8gaGF2ZSBhbW9uZyB0aGUgbW9zdCBtaXRvY2hvbmRyaWEgYW1vbmcgbWFtbWFsaWFuIGNlbGxzLCB0aGVzZSBkYXRhIG1heSBzaG93IGhpZ2hlciBwZXJjZW50YWdlIG9mIG1pdG9jaG9uZHJpYWwgZ2VuZXMgdGhhbiBtb3N0IGRhdGFzZXRzLg0KDQotICAgU2tpbiBjZWxscyB3aGljaCBhcmUgbGVzcyBtZXRhYm9saWNhbGx5IGFjdGl2ZQ0KDQogICAgR2l2ZW4gdGhhdCB0aGVzZSBhcmUgbGVzcyBtZXRhYm9saWNhbGx5IGFjdGl2ZSwgdGhleSBtYXkgYmUgZXhwZWN0ZWQgdG8gc2hvdyBzaW1pbGFyIG9yIGxvd2VyIHBlcmNlbnRhZ2Ugb2YgbWl0b2Nob25kcmlhbCBnZW5lcy4NCg0KKipDb3JyZWxhdGlvbiBvZiBjb3VudHMgYW5kIG1pdG9jaG9uZHJpYWwgY291bnRzKioNCg0KQXMgd2l0aCB0aGUgZmVhdHVyZXMgYW5kIGNvdW50cywgd2UgY2FuIGxvb2sgYXQgaG93IHRoZXNlIGNvcnJlbGF0ZToNCg0KYGBge3J9DQpGZWF0dXJlU2NhdHRlcihzZXVyYXRfb2JqZWN0LCBmZWF0dXJlMSA9ICduQ291bnRfUk5BJywgZmVhdHVyZTIgPSAncGVyY2VudC5tdCcpDQpgYGANCg0KSW50ZXJlc3RpbmdseSwgdGhlc2UgYXJlIG5vdCBjb3JyZWxhdGVkIGF0IGFsbC4gUHJpb3IgdG8gZmlsdGVyaW5nLCB0aGVzZSB2YWx1ZXMgd2lsbCBiZSBhbnRpLWNvcnJlbGF0ZWQgYXMgbWl0b2Nob25kcmlhIG9mdGVuIG1ha2VzIHVwIHRoZSBhbWJpZW50IFJOQSAocmVsZWFzZSBmcm9tIGRlYWQgb3IgbHlzZWQgY2VsbHMpIGFuZCB3aWxsIGJlIG1vcmUgcmVwcmVzZW50ZWQgaW4gdGhlIGJhY2tncm91bmQgKGxvdyBjb3VudCBjZWxscykuDQoNCiMjIyA0LjMuIEFtYmllbnQgUk5BIGNvbnRhbWluYXRpb24NCg0KQXMgd2l0aCBhbnkgc2luZ2xlLWNlbGwgdHJhbnNjcmlwdG9taWMgdG9vbCwgdGhlcmUgYXJlIGRvemVucywgaWYgbm90IG1vcmUsIG9wdGlvbnMgZm9yIHJlbW92YWwgb2YgYWJpZW50IFJOQS4gR2VuZXJhbGx5LCB0aGUgY29uY2VwdCBmb3IgYW1iaWVudCBSTkEgcmVtb3ZhbCBpcyB0aGUgc2FtZSAtIHN1YnN0cmFjdCBnZW5lcyB3aGljaCBhcmUgcHJlc2VudCBpbiB0aGUgKmVtcHR5KiBHRU1zL3dlbGxzIGZyb20gdGhvc2Ugd2hpY2ggYXJlIGZvdW5kIHRvIGhhdmUgY2VsbHMuIE90aGVyIHRlY2huaXF1ZXMgc3VjaCBhcyBjb21iaW5hdG9yaWFsIGJhcmNvZGluZyBhcHByb2FjaGVzIGhhdmUgdG8gbWFrZSBkaWZmZXJlbnQgYXNzdW1wdGlvbnMuDQoNCiFbPGh0dHBzOi8vd3d3LnNjLWJlc3QtcHJhY3RpY2VzLm9yZy9wcmVwcm9jZXNzaW5nX3Zpc3VhbGl6YXRpb24vcXVhbGl0eV9jb250cm9sLmh0bWw+XShmaWd1cmVzL2FtYmllbnRSTkEucG5nKXt3aWR0aD0iNTIwIn0NCg0KW1NvdXBYXShodHRwczovL2FjYWRlbWljLm91cC5jb20vZ2lnYXNjaWVuY2UvYXJ0aWNsZS85LzEyL2dpYWExNTEvNjA0OTgzMSkgaXMgb25lIGV4YW1wbGUgb2YgYSB0b29sIHdoaWNoIGNsZWFucyB1cCB0aGUgKnNvdXAqIChmbG9hdGluZyBhbWJpZW50IFJOQSkgZnJvbSBjZWxscy4gVGhpcyB0b29scyB1c2VzIHRoZSBtb3N0IHJhdyBmb3JtYXQgb2YgZGF0YSB3aGljaCBpcyBnZW5lcmF0ZSBiZWZvcmUgYSB0aHJlc2hvbGQgaXMgc2V0IG9uIHRoZSBrbmVlIHBsb3QgZGlzY3Vzc2VkIGFib3ZlLiBGb3IgMTBYIEdlbm9taWNzIHRoZXNlIGFyZSBzdG9yZWQgaW4gdGhlIGByYXdfZmVhdHVyZV9iY19tYXRyaXhgIGFuZCBgZmlsdGVyZWRfZmVhdHVyZV9iY19tYXRyaXhgIGZvbGRlcnMuDQoNClRoaXMgdHV0b3JpYWwgd2lsbCBub3QgZ28gdGhyb3VnaCB0aGUgZW50aXJlIGFtYmllbnQgUk5BIHJlbW92YWwgd29ya2Zsb3cuIEV4Y2VsbGVudCBleGFtcGxlcyBjYW4gYmUgZm91bmQgW2hlcmVdKGh0dHBzOi8vY2VsbGdlbmkuZ2l0aHViLmlvL25vdGVib29rcy9odG1sL25ldy0xMGtQQk1DLVNvdXBYLmh0bWwpIGFuZCBbaGVyZV0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL1NvdXBYL3ZpZ25ldHRlcy9wYm1jVHV0b3JpYWwuaHRtbCkuDQoNCiMjIyA0LjQgRG91YmxldHMNCg0KQW5vdGhlciBjb21tb24gY2hhbGxlbmdlIHdpdGggdXNpbmcgc2luZ2xlLWNlbGwgdHJhbnNjcmlwdG9taWMgcGxhdGZvcm1zIGlzIG9uZSB0aGF0IGNhbiBhZmZlY3QgbW9zdCBzaW5nbGUtY2VsbCBiYXNlZCB0ZWNobmlxdWVzIGluY2x1ZGluZyBmbG93IGN5dG9tZXRyeSAtIGRvdWJsZXRzLiBUaGVzZSBjYW4gYmUgYSByZXN1bHQgb2YgY2VsbHMgc3RpY2tpbmcgdG8gZWFjaCBvdGhlciBvciBiZWluZyB0b28gY2xvc2UgdG8gZWFjaCBvdGhlciBkdXJpbmcgdGhlICpzb3J0aW5nKiwgYW5kIHRoZSByYXRlIGlzIHR5cGljYWxseSBhIGZ1bmN0aW9uIG9mIHRoZSB0b3RhbCBudW1iZXIgb2YgY2VsbHMgZXhhbWluZWQgKG1vcmUgY2VsbHMgcmVzdWx0cyBpbiBtb3JlIGRvdWJsZXRzKS4NCg0KIVs8aHR0cHM6Ly93d3cuc2MtYmVzdC1wcmFjdGljZXMub3JnL3ByZXByb2Nlc3NpbmdfdmlzdWFsaXphdGlvbi9xdWFsaXR5X2NvbnRyb2wuaHRtbD5dKGZpZ3VyZXMvRG91YmxldHMucG5nKXt3aWR0aD0iOTk2In0NCg0KKk5vdGU6IFdlIHdpbGwgZGlzY3VzcyBkaW1lbnNpb24gcmVkdWN0aW9uIHdoaWNoIGlzIGNlbnRyYWwgdG8gdGhpcyBhcHByb2FjaCBpbiBhIGxhdGVyIG1vZHVsZS4qDQoNCkRvdWJsZXRzIGNhbiBjb25zaXN0IG9mIGVpdGhlciAyIGNlbGxzIG9mIHRoZSBzYW1lIHR5cGUgKGhvbW90eXBpYykgb3IgZGlmZmVyZW50IGNlbGwgdHlwZXMgKGhldGVyb3R5cGljKS4gQXMgeW91IGNhbiBwcm9iYWJseSBpbWFnaW5lLCBkaXN0aW5ndWlzaW5nIGhvbW90eXBpYyBkb3VibGV0cyBpcyBtb3JlIGNoYWxsZW5naW5nLCBidXQgYXJlIGdlbmVyYWxseSBjb25zaWRlcmVkIHRvIGJlIGxlc3Mgb2YgYSBjb25jZXJuLg0KDQpbRG91YmxldEZpbmRlcl0oaHR0cHM6Ly93d3cuY2VsbC5jb20vY2VsbC1zeXN0ZW1zL2Z1bGx0ZXh0L1MyNDA1LTQ3MTIoMTkpMzAwNzMtMCkgYW5kIFtzY0RibEZpbmRlcl0oaHR0cHM6Ly93d3cuY2VsbC5jb20vY2VsbC1zeXN0ZW1zL2Z1bGx0ZXh0L1MyNDA1LTQ3MTIoMTkpMzAwNzMtMCkgYXJlIFIgYmFzZWQgdG9vbCBmb3IgdGhlIGlkZW50aWZpY2F0aW9uIG9mIGRvdWJsZXRzIGJhc2VkIG9uIHRoZSBnZW5lcmFsIHByaW5jaXBsZSBvdXRsaW5lZCBhYm92ZS4gVGhpcyBleGFtcGxlIHVzZXMgc2NEYmxGaW5kZXI6DQoNCmBgYHtyfQ0Kc2NlID0gYXMuU2luZ2xlQ2VsbEV4cGVyaW1lbnQoc2V1cmF0X29iamVjdCkgIyBXZSBuZWVkIHRvIGNvbnZlcnQgdGhlIFNldXJhdCBmb3JtYXQgdG8gU2luZ2xlQ2VsbEV4cGVyaW1lbnQgZm9ybWF0DQpzY2UgPC0gc2NEYmxGaW5kZXIoc2NlKSAjIFJ1biBzY0RibEZpbmRlcg0KYGBgDQoNCk5leHQgd2Ugd2lsbCB0cmFuc2ZlciB0aGUgaW5mb3JtYXRpb24gZ2FpbmVkIGZyb20gdGhpcyBhbmFseXNpcyBpbnRvIHRoZSBTZXVyYXQgb2JqZWN0IHNvIHRoYXQgd2UgY2FuIHZpc3VhbGl6ZSBpdCBhIGxhdGVyIHN0ZXAuDQoNCmBgYHtyfQ0KIyBUcmFuc2ZlciB0aGUgZG91YmxldCBpbmZvcm1hdGlvbiB0byB0aGUgc2V1cmF0IG9iamVjdA0Kc2V1cmF0X29iamVjdCRzY0RibEZpbmRlci5jbGFzcyA9IGNvbERhdGEoc2NlKSRzY0RibEZpbmRlci5jbGFzcw0Kc2V1cmF0X29iamVjdCRzY0RibEZpbmRlci5zY29yZSA9IGNvbERhdGEoc2NlKSRzY0RibEZpbmRlci5zY29yZQ0KDQpgYGANCg0KIyMjIDQuNS4gRmlsdGVyaW5nIGRhdGENCg0KVWx0aW1hdGVseSwgdGhlIHB1cnBvc2Ugb2YgdGhlIFFDIGFuYWx5c2VzIGlzIG5vdCBvbmx5IHRvIHVuZGVyc3RhbmQgeW91ciBkYXRhIGFuZCBmaW5kIGFueSBtYWpvciBpc3N1ZXMuIEl0IGlzIGFsc28gdXNlZCB0byBpZGVudGlmeSBpbmRpdmlkdWFsIGRhdGEgcG9pbnRzIHdoaWNoIGFyZSBsaWtlbHkgcHJvYmxlbWF0aWMgYW5kIGNvdWxkIG5lZ2F0aXZlbHkgaW1wYWN0IHN1YnNlcXVlbnQgYW5hbHlzZXMuIEJlY2F1c2Ugd2Ugb2Z0ZW4gaGF2ZSB0aG91c2FuZHMgb2YgZGF0YSBwb2ludHMsIHdlIGdlbmVyYWxseSBhY2NlcHQgdGhhdCBzb21lIG9mIHRoZXNlIGRhdGEgcG9pbnRzIGFyZSB0ZWNobmljYWwgbm9pc2Ugd2hpY2ggY2FuIHNpbXBseSBiZSByZW1vdmVkLiBGb3IgZXhhbXBsZSwgaGVyZSB3ZToNCg0KLSAgIFJlbW92ZSBjZWxscyB3aXRoIGV4dHJlbWVseSBoaWdoIG9yIGxvdyBjb3VudHMgYXMgZWl0aGVyIGVtcHR5IGRyb3BsZXRzIG9yIG11bHRpcGxldHMuDQoNCi0gICBSZW1vdmUgY2VsbHMgd2l0aCBtb3JlIHRoYW4gNSUgb2YgcmVhZHMgZnJvbSBtaXRvY2hvbmRyaWFsIGdlbmVzIGFzIGFuIGluZGljYXRvciBvZiB1bmhlYWx0aHkgY2VsbHMuDQoNCi0gICBSZW1vdmUgY2VsbHMgY2xhc3NpZmllZCBhcyBkb3VibGV0cy4NCg0KYGBge3J9DQpzZXVyYXRfb2JqZWN0IDwtIHN1YnNldChzZXVyYXRfb2JqZWN0LCBzdWJzZXQgPSBuRmVhdHVyZV9STkEgPiAyMDAgJiBuRmVhdHVyZV9STkEgPCAyNTAwICYgcGVyY2VudC5tdCA8IDUgJiBzY0RibEZpbmRlci5jbGFzcyA9PSAnc2luZ2xldCcpDQoNCnByaW50KHNldXJhdF9vYmplY3QpDQpgYGANCg0KIyMgNS4gTm9ybWFsaXphdGlvbg0KDQpGaW5hbGx5IHdlIHdhbnQgdG8gYWRkcmVzcyB0aGUgaXNzdWUgb2YgdW5lcXVhbCBzZXF1ZW5jaW5nIGRlcHRoIGFuZCBvdGhlciB0ZWNobmljYWwgbm9pc2UgYnkgcGVyZm9ybWluZyBub3JtYWxpemF0aW9uLiBIZXJlIHdlIG5vcm1hbGl6ZSB0aGUgZGF0YSB1c2luZyBhICpyZWd1bGFyaXplZCBuZWdhdGl2ZSBiaW5vbWlhbCByZWdyZXNzaW9uKiB3aGljaCBtb2RlbHMgYWRqdXN0cyB0aGUgY291bnRzIGZvciBlYWNoIGdlbmUgYmFzZWQgb24gdGhlIHRvdGFsIG51bWJlciBvZiBjb3VudHMgd2l0aGluIGEgY2VsbCB3aXRoIGFkanVzdG1lbnRzIGZvciBsb3dseSBhbmQgaGlnaGx5IGV4cHJlc3NlZCBnZW5lcyBhcyBbZGVzY3JpYmVkIGhlcmVdKGh0dHBzOi8vZ2Vub21lYmlvbG9neS5iaW9tZWRjZW50cmFsLmNvbS9hcnRpY2xlcy8xMC4xMTg2L3MxMzA1OS0wMTktMTg3NC0xKS4NCg0KYGBge3J9DQpzZXVyYXRfb2JqZWN0ID0gU0NUcmFuc2Zvcm0oc2V1cmF0X29iamVjdCwgdmVyYm9zZSA9IEYpDQpgYGANCg0KRmluYWxseSB3ZSB3YW50IHRvIHZpc3VhbGl6ZSB0aGUgZGF0YS4gVG8gZG8gdGhpcyB3ZSBuZWVkIHRvIHBlcmZvcm0gZGltZW5zaW9uIHJlZHVjdGlvbiB3aGljaCB3aWxsIGJlIGNvdmVyZWQgaW4gYSBsYXRlciBtb2R1bGUuIFdlIHdpbGwgcnVuIGl0IGhlcmUgZm9yIHZpc3VhbGl6YXRpb24gcHVycG9zZXMuDQoNCmBgYHtyfQ0Kc2V1cmF0X29iamVjdCA9IFJ1blBDQShzZXVyYXRfb2JqZWN0LCB2ZXJib3NlID0gRikNCnNldXJhdF9vYmplY3QgPSBSdW5VTUFQKHNldXJhdF9vYmplY3QsIGRpbXMgPSAxOjMwLCB2ZXJib3NlID0gRikNCkZlYXR1cmVQbG90KHNldXJhdF9vYmplY3QsIGZlYXR1cmVzID0gYygnc2NEYmxGaW5kZXIuc2NvcmUnLCAncGVyY2VudC5tdCcsICduRmVhdHVyZV9STkEnLCAnbkNvdW50X1JOQScpKQ0KYGBgDQoNCmBgYCAgICAgICAgIA0KNi4gRGltZW5zaW9uIHJlZHVjdGlvbiBhbmQgY2x1c3RlcmluZzsgNy4gQW5ub3RhdGlvbiBvZiBjZWxsIHR5cGVzOyA4LiBUcmFqZWN0b3JpZXMgYW5kIFJOQSB2ZWxvY2l0eTsgOS4gSW5mZXJyaW5nIGNlbGwtY2VsbCBjb21tdW5pY2F0aW9uOyAxMC4gU3BhdGlhbCB0cmFuc2NyaXB0b21pY3MNCmBgYA0K